package com.scs.application.modules.msg.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.dto.WebSocketMsgDTO;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.core.utils.UtilCommon;
import com.scs.application.core.utils.UtilWebsocket;
import com.scs.application.modules.msg.dto.PushDTO;
import com.scs.application.modules.msg.entity.InnerInbox;
import com.scs.application.modules.msg.entity.InnerNotice;
import com.scs.application.modules.msg.enums.MessageType;
import com.scs.application.modules.msg.enums.ReceiverType;
import com.scs.application.modules.msg.model.*;
import com.scs.application.modules.msg.sender.EmailMessageSender;
import com.scs.application.modules.msg.sender.InnerNoticeMessageSender;
import com.scs.application.modules.msg.sender.SmsMessageSender;
import com.scs.application.modules.msg.service.InnerInboxService;
import com.scs.application.modules.msg.service.InnerNoticeService;
import com.scs.application.modules.msg.service.PushService;
import com.scs.application.modules.sys.entity.Role;
import com.scs.application.modules.sys.entity.RoleUser;
import com.scs.application.modules.sys.entity.User;
import com.scs.application.modules.sys.service.RoleService;
import com.scs.application.modules.sys.service.RoleUserService;
import com.scs.application.modules.sys.service.UserService;
import lombok.extern.log4j.Log4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 */
@Log4j
@Service
public class PushServiceImpl implements PushService {

    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    @Lazy
    private SmsMessageSender smsMessageSender;

    @Autowired
    @Lazy
    private EmailMessageSender emailMessageSender;

    @Autowired
    @Lazy
    private InnerNoticeMessageSender innerNoticeMessageSender;

    @Autowired
    @Lazy
    private UserService userService;

    @Autowired
    @Lazy
    private RoleService roleService;

    @Autowired
    @Lazy
    private RoleUserService roleUserService;

    @Autowired
    @Lazy
    private InnerNoticeService innerNoticeService;

    @Autowired
    @Lazy
    private InnerInboxService innerInboxService;

    @Override
    @Async
    public void pushSms(SmsMessage message) {
        smsMessageSender.send(message);
    }

    @Override
    @Async
    public void pushEmail(EmailMessage message) {
        emailMessageSender.send(message);
    }

    @Override
    public void pushInnerNotice(InnerNoticeMessage message) {
        innerNoticeMessageSender.send(message);
    }


    @Override
    public void pushByDTO(PushDTO pushDTO) {
        List<Message> messages = buildMessage(pushDTO);

        for (Message message : messages) {
            if (message instanceof EmailMessage) {
                this.pushEmail((EmailMessage) message);
            } else if (message instanceof SmsMessage) {
                this.pushSms((SmsMessage) message);
            } else if (message instanceof InnerNoticeMessage) {
                this.pushInnerNotice((InnerNoticeMessage) message);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pushMessage(PushDTO pushDTO) {
        //同一refid 只推送一次
        if (!UtilCommon.strIsNull(pushDTO.getRefId())) {
            List<InnerNotice> innerNotices = innerNoticeService.list(Wrappers.<InnerNotice>query().eq("refid",pushDTO.getRefId()));
            if (innerNotices!=null && innerNotices.size() >0) return;

        }
        List<User> users = null;
        if ("all".equals(pushDTO.getReceiverType())) {
            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
            );
        } else if ("dept".equals(pushDTO.getReceiverType())) {

            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
                    .in("office_id", StringUtils.split(pushDTO.getReceiverCodes(), ",")
                    ));
        } else if ("role".equals(pushDTO.getReceiverType())) {
            List<Role> roles = roleService.list(Wrappers.<Role>query()
                    .in("code", StringUtils.split(pushDTO.getReceiverCodes(), ",")));
            if (roles == null || roles.size() <1) return;

            List<RoleUser>  roleUserIds= roleUserService.list(Wrappers.<RoleUser>query()
                    .in("role_id", roles.stream().map(Role :: getId).collect(Collectors.toList())
                    ));
            if (roleUserIds == null || roleUserIds.size() <1) return;
            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
                    .in("id", roleUserIds.stream().map(RoleUser :: getUserId).collect(Collectors.toList())
                    ));
            if (Arrays.asList(pushDTO.getReceiverCodes().split(",")).contains("admin")) {
                User userAdmin = new User();
                userAdmin.setCode("admin").setName("管理员").setId("111111");
                users.add(userAdmin);
            }
            if (users == null || users.size() <1) return;
        }
        List<InnerInbox> innerInboxes = Lists.newArrayListWithCapacity(users.size());
        String userCodes = users.stream().map(o -> o.getCode()).collect(Collectors.joining(","));
        String userNames = users.stream().map(o -> o.getName()).collect(Collectors.joining(","));

        InnerNotice innerNotice = new InnerNotice();
        if ("all".equals(pushDTO.getReceiverType()) ){
            innerNotice.setReceiverGroup("全体用户");
        }else if("dept".equals(pushDTO.getReceiverType()) || "role".equals(pushDTO.getReceiverType())){
            innerNotice.setReceiverGroup(pushDTO.getReceiverNames());
        }
        innerNotice.setTitle(pushDTO.getTitle())
                .setContent(pushDTO.getContent())
                .setContentType(pushDTO.getContentType())
                .setReceiverType(pushDTO.getReceiverType())
                .setReceiverCodes(userCodes)
                .setReceiverNames(userNames)
                .setGmtSend(new Date()).setRefid(pushDTO.getRefId());
        if (com.scs.application.core.utils.StringUtils.isBlank(innerNotice.getContent())) {
            innerNotice.setContent("无");
        }
        //由定时任务或者ws调用的时候，不存在SecurityManager
        try {
            innerNotice.setSender(UserUtils.currentUser()!=null ? UserUtils.currentUser().getCode() : "system");
        }catch (Exception e) {
            innerNotice.setSender("system");
        }
        innerNotice.setSender(UtilCommon.getStr(innerNotice.getSender(),"system"));

        innerNoticeService.save(innerNotice);

        for(User u : users){
            InnerInbox inbox = new InnerInbox();
            inbox.setInnerNoticeId(innerNotice.getId())
                    .setReceiverUserCode(u.getCode())
                    .setReceiverUserName(u.getName())
                    .setIsRead(GlobalConsts.NO)
                    .setGmtRead(null);
            innerInboxes.add(inbox);
        }
        if(!innerInboxes.isEmpty()){
            innerInboxService.saveBatch(innerInboxes);
        }

        log.debug("站内信发送成功，接收人：{}", userCodes);

        //此处不使用注解，因为有静态调用
        WebSocketMsgDTO webSocketMsg = new WebSocketMsgDTO();
        webSocketMsg.setType(GlobalConsts.WebSocketMsgType.PARAM_CHANGE)
                .setTypeSub(GlobalConsts.MarkParameterTYpe.USER_MSG)
                .setReceiver(userCodes)
                .setNoticeId(innerNotice.getId())
                .setMsg("用户站内消息");
        UtilWebsocket.sendTextAll(webSocketMsg);
    }

    @Transactional(rollbackFor = Exception.class)
    @Async
    @Override
    public void pushMessageAsync(PushDTO pushDTO) {
        this.pushMessage(pushDTO);
    }


    private List<Message> buildMessage(PushDTO pushDTO) {

        ReceiverType receiverType = ReceiverType.of(pushDTO.getReceiverType());

        List<User> users = null;
        if (receiverType == ReceiverType.USER) {
            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
                    .in("code", StringUtils.split(pushDTO.getReceiverCodes(), ",")
                    ));
        } else if (receiverType == ReceiverType.ALL) {
            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
            );
        } else if (receiverType == ReceiverType.DEPT) {
            users = userService.list(Wrappers.<User>query()
                    .select("code,name,mobile")
                    .in("office_id", StringUtils.split(pushDTO.getReceiverCodes(), ",")
            ));
        }

        List<Message> messages = Lists.newArrayList();

        String[] messageTypes = pushDTO.getMessageTypes();
        for (String messageType : messageTypes) {
            MessageType type = MessageType.of(messageType);
            if (MessageType.EMAIL.equals(type)) {
                users.forEach(o -> {
                    EmailMessage emailMessage = new EmailMessage();
                    emailMessage.setTo(o.getEmail());
                    messages.add(emailMessage);
                });
            } else if (MessageType.INNER.equals(type)) {
                InnerNoticeMessage message = new InnerNoticeMessage();
                //message.setPriority(InnerNoticePriority.HIGHER);
                //message.setContentType(InnerNoticeContentType.ANNOUNCE);
                message.setContentType(pushDTO.getContentType());
                message.setReceiverType(pushDTO.getReceiverType());
                if (receiverType == ReceiverType.ALL){
                    message.setReceiverGroup("全体用户");
                }else if(receiverType == ReceiverType.DEPT){
                    message.setReceiverGroup(pushDTO.getReceiverNames());
                }
                message.setReceiverCodes(users.stream().map(o -> o.getCode()).collect(Collectors.joining(",")));
                message.setReceiverNames(users.stream().map(o -> o.getName()).collect(Collectors.joining(",")));
                message.setSender(UserUtils.currentUser().getCode());
                messages.add(message);
            } else if (MessageType.SMS.equals(type)) {
                AliyunSmsMessage message = new AliyunSmsMessage();
                message.setMobiles(users.stream().map(o -> o.getMobile()).collect(Collectors.joining(",")));
                messages.add(message);
            }
        }

        for (Message message : messages) {
            message.setTitle(pushDTO.getTitle());
            message.setContent(pushDTO.getContent());
        }

        return messages;
    }

}
